
/**
*
* Show Minifier
* Author: Shihua Ma <beijing.josh@gmail.com>
* Copyright (c) 2010 Shihua Ma
* Licensed under the MIT license.
*
*/

(function(window){
	var radom = Math.floor(Math.random() * 62586831288).toString(36),
		location = window.location.href,
		SERVER = window.location.protocol + "//" + window.location.host,
		$ = function(id) { return typeof id == "string" ? document.getElementById(id) : id; },
		isIE = window.ActiveXObject,
		urlReg = /^(http[s]?:\/\/[-\w]+(\.\w[-\w]*)+(:\d+)?[\/]?(?:[^;\"'<>()\[\]{}\s\x7F-\xFF!.,?]*)(?:[!.,?]+[^;\"'<>()\[\]{}\s\x7F-\xFF!.,?]+)*)$/i,
		performanceTmpl = "<table>" +
					"<caption>Current performance test report" + 
					"<thead><tr><th>Original page parser time<th>Minified page parser time<th>Original network latency<th>Minified network latency<th>Original page reflow time<th>Minified page reflow time" +
					"<%= !(result.oReflowTime || result.mReflowTime) ? '<tfoot><tr><td calspan=\"6\"><a id=\"reflow\" href=\"#\">Do Reflow</a>' : '' %>" +
					"<tbody><tr><td><%= result.oTime %>MS<td><%= result.mTime %>MS<td><%= result.oLatency %>MS<td><%= result.mLatency %>MS<td><%= result.oReflowTime ? result.oReflowTime +'MS' : '' %>&nbsp;<td><%= result.mReflowTime ? result.mReflowTime +'MS' : '' %>&nbsp;"
					"</table>",
		dislayStyle = "#s\\:m *{margin:0;padding:0;text-align:left;}#s\\:m table{*border-collapse:collapse;border-spacing:0;font-size:12px;line-height:1.5;}#s\\:m caption,#s\\:m th{text-align:left;font-style:normal;}#s\\:m a,#s\\:m a:hover{margin-left:20px;color:#0000EE!important;;font-size:16px!important;}#s\\:m div{text-align:center;}#s\\:m img,#s\\:m a img{border:0;}#s\\:m li{list-style:none;}#s\\:m{border:1px solid #345191;width:680px!important;margin:0 auto!important;font-family:'tahoma','arial','sans-serif'!important;font-size:16px!important;line-height:16px!important;color:#333!important;background:#fff!important;position:relative!important;z-index:1000001!important;}#s\\:m ul{padding-bottom:0.8em;}#s\\:m p{margin:8px 5px 2px;border-bottom:1px solid #D5D5D5;font-weight:bold;text-indent:5px;cursor:pointer;}#s\\:m ul dl{margin:5px 10px;font-size:11px;}#s\\:m dt{float:left;width:110px;font-weight:bold;text-align:right;}#s\\:m dd{margin-left:120px;}#s\\:m table{width:100%;margin:5px 10px;}ul#sm\\:none\\:border{border:none;margin:5px 10px;padding-left:1em;}#sm\\:hide{position:absolute;right:0;color:#fff;background:#345191;padding: 0 1em;height:26px;line-height:26px;font-weight:bold;cursor:pointer;}#sm\\:show{font-family:'tahoma','arial','sans-serif'!important;font-size:16px!important;line-height:26px!important;top:0;color:#fff;background:#345191;padding: 0 1em;height:26px;font-weight:bold;position:fixed!important;z-index:1000002!important;cursor:pointer;display:none;}#s\\:m{}",
		displayShow = "<div id='sm:show'>show</div>",
		displayTmpl = '<div id="s:m"><div id="sm:hide"><span>hide</span></div><div style="padding-top:5px;"><a href="<%= server %>/" title="Go to Home"><img src="<%= server %>/img/logo.png" width="202" height="27" alt="logo"></a></div><ul><li><p>Minified result</p><div><table><thead><tr><th>Original size<th>Minified size<th>Savings<th>Savings percent<tr><td><%= page.originalLength %>( <%= page.originalDeflatedLength %> gzipped )<td><%= page.minifiedLength %>( <%= page.minifiedDeflatedLength %> gzipped )<td><%= page.savings %>( <%= page.deflatedSavings %> gzipped )<td><%= page.minfiedPercent %>( <%= page.deflatedPercent %> gzipped )</table><div><div><a target="_blank" href="<%= server %>/display?type=source&view=o&key=<%= key %>">View source of original page</a><a target="_blank" href="<%= server %>/display?type=source&key=<%= key %>">View source of minified page</a></div><div><a href="<%= server %>/evaluate?key=<%= key %>">Evaluate page parser performance</a><a href="<%= server %>/evaluate?reflow=true&key=<%= key %>">Evaluate page parser performance with reflow time</a></div><div><a href="<%= server %>/performance/detail?key=<%= key %>">View performance results</a><a id="h:m:options" href="#">Let HTML Minifier follow the page links</a></div></div></div><li><p>Request headers<dl><% for (var i=0 , item = page.requestHeader , len = item.length; i < len; i++) {print("<dt>", item[i].name, "<dd>" ,item[i].value);} %></dl><li><p>Response headers<dl><% for (var i=0 , item = page.responseHeader , len = item.length; i < len; i++) {print("<dt>", item[i].name, "<dd>" ,item[i].value);} %></dl><li><p>Minified optionns<ul id="sm:none:border"><li>Remove comments ( <%= options["remove-comments"] %> )<li>Remove comments from scripts and styles ( <%= options["remove-comments-from-cdata"] %> )<li>Remove CDATA sections from scripts and styles ( <%= options["remove-cdata-sections-from-cdata"] %> )<li>Collapse whitespace ( <%= options["collapse-whitespace"] %> )<li>Collapse boolean attributes ( <%= options["collapse-boolean-attributes"] %> )<li>Remove attribute quotes ( <%= options["remove-attribute-quotes"] %> )<li>Remove redundant attributes/values ( <%= options["remove-redundant-attributes"] %> )<li>Use short doctype ( <%= options["use-short-doctype"] %> )<li>Remove empty (or blank) attributes ( <%= options["remove-empty-attributes"] %> )<li>Remove optional tags ( <%= options["remove-optional-tags"] %> )<li>Remove URLSchmeme ( <%= options["remove-url-scheme"] %> )<li>Remove type="text/javascript" from script"s ( <%= options["remove-script-type-attributes"] %> )<li>Remove type="text/css" from style"s ( <%= options["remove-style-type-attributes"] %> )<li>Remove empty elements ( <%= options["remove-empty-elements"] %> )</ul></ul></div>',
		report = {},
		insertCssText = function(cssText, opt_id) {
			var cssNode = document.createElement('style');
			cssNode.type = 'text/css';
			cssNode.id = opt_id ? opt_id : 'cssh-sheet-' + document.styleSheets.length;
			var headEl = document.getElementsByTagName('head')[0] || document.documentElement;
			headEl.insertBefore(cssNode,headEl.lastChild);
			if (cssNode.styleSheet) {
			  cssNode.styleSheet.cssText = cssText;
			} else {
			  var cssText = document.createTextNode(cssText);
			  cssNode.appendChild(cssText);
			}
			return cssNode;
		},
		jsonp = function(url,callback){	
			var jsonp_ = "jsonp" + (new Date).getTime(), 
				doc = document,
				header =  doc.getElementsByTagName('head')[0] || doc.documentElement,
				script = doc.createElement('script');
			window[ jsonp_ ] = function(date){
				callback(date);
				window[ jsonp ] = undefined;
				try{ delete window[ jsonp ]; } catch(e){}
				header.removeChild( script );
			}
			url = /callback=/.test(url) ? url :
				url + (/\?/.test(url) ? "&" : "?" ) + "callback=" + jsonp_;
			script.src = url ;
			header.insertBefore( script, header.firstChild );
		},
		evantID = 0;
		eventCache = {};
		fixEvent = function(e){
			if ( !e.preventDefault ) {
				e.preventDefault = function(){
					e.returnValue = false;
				}
			}
			if (!e.stopPropagation ) {
				e.stopPropagation = function(){
					e.cancelBubble = true;
				}
			}
			return e;
		},
		addEvent = function(el, type, listener) {
			if (window.attachEvent) {
				el.attachEvent("on" + type, function() {
					listener.call(el,fixEvent(window.event));
				});
			} else {
				el.addEventListener(type, listener, false);
			}
		},
		userAgent = navigator.userAgent.toLowerCase(),
		browser = {
			/*for Firefox,Safari,Chrome,Opera,IE*/
			version: (/.+(?:ox|on|me|ra|ie)[\/: ]([\d.]+)/.exec(userAgent) || [0,'0'])[1],
			/*ignore some browser(e.g. Maxthon,Iron,OmniWeb)*/
			safari: /webkit/.test( userAgent ) && !/chrome/.test( userAgent ),
			opera: /opera/.test( userAgent ),
			msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
			firefox: /firefox/.test( userAgent ) && !/opera/.test( userAgent ),
			chrome : /chrome/.test( userAgent ),
			mobile : /mobile/.test(userAgent),
			android : this.mobile && /android/.test(userAgent),
			ipod : this.mobile && /ipod/.test(userAgent),
			iphone : this.mobile && /iphone/.test(userAgent),
			ipad : this.mobile && /ipad/.test(userAgent)
		},
		getParam = function(param) {
			param = param.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]');
			var regexString = '[\\?&]' + param + '=([^&#]*)';
			var regex = new RegExp(regexString);
			var results = regex.exec(location);
			if (results == null) {
			  return '';
			} else {
			  return results[1];
			}
		},
		curry = function(f, n) {
			if (typeof(n) == 'undefined') n = f.length;
			var args = arguments;
			var _this = this;
			return function () {
				if (arguments.length >= n) {
					return f.apply(_this, arguments);
				} else {
					var a = [].slice.call(arguments,0);
					return args.callee.call(_this,function () {
						return f.apply(_this, a.concat([].slice.call(arguments,0)))
					},n - a.length);
				}
			};
		},
		each = function (o, f, args){
			if(Object.prototype.toString.apply(o) === '[object Array]')
				for ( var i = 0, ol = o.length , val = o[0];
					i < ol && f.apply(val, args ? args : [i,val]) !== false; val = o[++i] );
			else
				for ( var i in o ){
					var value =  f.apply( o[i], args ?  args : [i, o[i]]);
					if(value === false){
						break;
					}
				}
		},
		setCookie = function (name, value, options){
			var text = "";
			if(!!options){
				text += (options.expires instanceof Date ? ";expires=" + options.expires.toUTCString() : "") +
						(options.path ? ";path=" + options.path : "" ) +
						(options.domain ? ";domain=" + options.domain : "" ) +
						(options.secure ? ";secure" : "" );
			}			
			window.document.cookie =  encodeURIComponent(name) + "=" +
				encodeURIComponent( value ) +
				text;
		},
		getCookie = function(name) {
			var all_cookies = document.cookie.split(';'),
				temp_cookie = '',
				cookie_name = '',
				cookie_value = '',
				cookie_found = false;

			for (var i = 0, len = all_cookies.length; i < len; i++ ){
				temp_cookie = all_cookies[i].split( '=' );
				cookie_name = temp_cookie[0].replace(/^\s+|\s+$/g, '');
				if ( cookie_name == name ){
					cookie_found = true;
					if ( temp_cookie.length > 1 ){
						cookie_value = encodeURIComponent( temp_cookie[1].replace(/^\s+|\s+$/g, '') );
					}
					return cookie_value;
					break;
				}
				temp_cookie = null;
				cookie_name = '';
			}
			if ( !cookie_found ){
				return null;
			}
		},
		removeCookie =  function(name){
			setCookie(name,value,{expires:new Date(0)});
		},
		cache = [],
		tmpl = function tmpl(str, data){
			var fn = !/\W/.test(str) ? 
					cache[str] = cache[str] || tmpl($(str).innerHTML) :
					new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};" +
						"with(obj){p.push('" +
						str.replace(/[\r\t\n]/g, "")
						.split("<%").join("\t")
						.replace(/((^|%>)[^\t]*)'/g, "$1\r")
						.replace(/\t=(.*?)%>/g, "',$1,'")
						.split("\t").join("');")
						.split("%>").join("p.push('")
						.split("\r").join("\\'") +
						"');}return p.join('');"
					);
			return data ? fn( data ) : fn;
		},
		Linear ={
			easeIn: function (t, b, c, d) {
				return c*t/d + b;
			},
			easeOut: function (t, b, c, d) {
				return c*t/d + b;
			},
			easeInOut: function (t, b, c, d) {
				return c*t/d + b;
			}
		},
		Tasks = {
			_id: 0,
			_tasks: [],
			_dalay:25,
			start: function(){
				if (this._id )return;
				var self = this;
					(function(){
					  var t = self._tasks;
						for ( var i = 0; i < t.length; i++ )
							//remove the function from tasks
							if ( t[i]() === false ) {							
								t.splice(i, 1);
								i--;
							}
						self._id = setTimeout(arguments.callee, self._dalay );
					})();
			},
			stop: function(){
				clearTimeout( this._id);
				this._id = 0;
			},
			add: function(fn){
				this._tasks.push(fn);
				this.start();
			}
		},
		Dom = {};

	//simply do it
	each(["width",'height','offsetWidth','offsetHeight','offsetLeft','offsetTop'],function(i,v){
		Dom[v] = function(elem){
			return elem[v] || elem.style[v];
		}
	});

	function extend(d, s) {
		for (p in s) {
			d[p] = s[p];
		}
		return d;
	};

	function isDisplay(){
		return /^http[s]?:\/\/[^\/]*\/display\?/i.test(location);
	}

	function isReflow(){
		return getParam("reflow") === "true";
	};

	function isTest(){
		return getParam("performance") === "true";
	};

	function isMinifiedPage(){
		return !getParam("view");
	};

	function _getNode(node,dire,attr){
		var props = {
			"for": "htmlFor",
			"class": "className",
			readonly: "readOnly",
			maxlength: "maxLength",
			cellspacing: "cellSpacing",
			rowspan: "rowSpan",
			colspan: "colSpan",
			tabindex: "tabIndex"
		};
		if ( node.nodeType !== 1 && node.nodeType !== 9 ) {
			return null;
		}
		while((node = dire === "prev" ? node.previousSibling : 
							 dire === "next" ? node.nextSibling :
							 dire === "parent" ? node.parentNode :
							 null) 
				&& node.nodeType !== 1
				|| attr  && (new RegExp(attr.value,'i')).test(node.getAttribute(props[attr.name] || attr.name)))
				{
				}
		return !!node ? node : null;
	}

	function next(node, attr){
		return _getNode(node, 'next', attr);
	}

	function prev(node, attr){
		return _getNode(node, 'prev', attr);
	}

	function getElementsByClassName(container,name){
		 var elems = nodeToArray(container.getElementsByTagName('*')),
			result = [],
			reg = new RegExp('\s*' + name + '\s*');
		 each(elems,function(i, v){
			if(reg.test(v.className)){
				result.push(v);
			}
		 })
		 return result;
	}

	function containIn(container,ele){
		do{
			if(container == ele)
				return true;
		}while(ele = ele.parentNode)
		return false;
	}

	function arrayReduce(all, exclude){
		var val = [];
		for(var i = 0 , len = all.length; i < len ; i++){
			var found = false,elem = all[i];
			for(var m = 0 , length = exclude.length ; m < length; m++){
				if(exclude[m] === elem){
					found = true;
					break;
				}
			}
			if(!found){
				val.push(elem);
			}
		}
		return val;
	}
	function nodeToArray(nodes){
		var val = [];
		!isIE ? (val = [].slice.call(nodes)) : 
			(function(node){
				for(var i = 0 , len = nodes.length ; i < len ; i++){
					val.push(nodes[i]);
				}
			}(nodes));
		return val;
	}

	function showDetail(){
		insertCssText(dislayStyle);
		var doc = window.document,
			div = doc.createElement('div'),
			host = window.location.protocol + "//" + window.location.host,
			result = doc.createElement('div'),
			show = doc.createElement('div');
		result.style.position = "fixed";
		result.style.top = "0";
		result.style.zIndex='1000001';
		show.style.position = "absolute";
		doc.body.appendChild(result);
		doc.body.appendChild(show);
		show.appendChild(doc.createTextNode('show'));	
		show.id="sm:show";

		jsonp( SERVER + '/display?key=' + getParam("key") + "&type=json" , function(json){
			result.innerHTML = tmpl(displayTmpl, json);
				var elem = $('s:m'),
					pelem = nodeToArray(elem.getElementsByTagName('p')),
					move = Dom.offsetWidth(doc.body) - Dom.offsetWidth(elem),
					length = move / 2 + Dom.offsetWidth(elem),
					server = json.server,
					options = json.options;
				elem.style.left = (move / 2) + "px";
				each(pelem,function(i,v){
					var node = next(v);
					if(i != 0){
						 node.style.display = "none";
					}
					addEvent(v, "click", function(e){
						node && (style = node.style) && (node.e = style.display || 'block') && (style.display = node.e === 'none'  ? "block" : "none");
					});
				});
				addEvent($("sm:hide"),'click',function(e){
					var t = 0 , d = 100;
					Tasks.add(function(){
						 elem.style.left = Linear.easeIn(t,0,-(length),d) + "px";
						 if(t++ >= d ){
							show.style.display = "block";
							return false;
						}
					});	
				});
				addEvent(show,'click',function(e){
					var t = 0 , d = 100;
					setTimeout(function(){
						show.style.display = "none"; 
					},100);
					Tasks.add(function(){
						 elem.style.left = Linear.easeIn(t,-(length), length + (move /2) ,d) + "px";
						 if(t++ >= d ){	
							return false;			
						}
					});
				});

				addEvent($("h:m:options"),'click',function(e){
					e.preventDefault();
					var href = arrayReduce(document.getElementsByTagName('a'),elem.getElementsByTagName('a') || []),
						option = '';
					each(options,function(i,v){
						option = option + (v ?  i + "=on&" : "");
					})
					each(href,function(i,v){
						if(!new RegExp("^" + server).test(v.href))
							v.href = server + "/htmlminify?" + (option + (option === '' ? '?' : '') + 'url=' + v.href );
					})
				});	

		});
	};


	function doReflow(callback){
		var test = 'testDisplay',
			r = new ReflowTimer(false);
		r.normalizeTimesToTest = false;
		r.tests = [test];
		r.onTestsComplete = function(medianReflowTimes) {
			var time = medianReflowTimes[test];
			var json =  isMinifiedPage() ? { 'mReflowTime':  time } : { 'oReflowTime': time };
			callback(json);
		};
		r.run();
	};

	function showPerformance(elem,json){
		extend(report,json);
		$(elem).innerHTML = tmpl(performanceTmpl,{result:report});
		if(!!$('reflow')){
			addEvent($('reflow'),'click',function(e){
				e.preventDefault();
				each(['original-frame','minified-frame'],function(i,v){
					$(v).contentWindow['_doReflow@Show:Minifier_'](function(json){
						window.parent.showPerformance('result',json);
					});
				});
			})
		}
	}

	function doPerformance(){
		if(!isTest())
			return;
		//var json = isMinifiedPage() ? "{ 'mTime' :"+  _minifier_performanceTime +"}" : "{ 'oTime':" +  _minifier_performanceTime + "}";
		//var time = window.name = json ;
		var serverTime = new Date(parseInt(getParam("sd"))),
			now = new Date,
			offset = now.getTime() - serverTime.getTime() > 0 ? now.getTime() - serverTime.getTime() :  serverTime.getTime() - now.getTime(),
			allTime;
			now.setTime(window['_startTime@Show:Minifier_']);
			allTime = now.getTime() - serverTime.getTime(),
			latency = now.getTime()-window['_pageSendTime@Show:Minifier_'],
			json = isMinifiedPage() ? { 'mTime' : window['_performanceTime@Show:Minifier_'] , 'mLatency' : latency } : { 'oTime':  window['_performanceTime@Show:Minifier_'] , 'oLatency': latency };
		try{
			window.parent.showPerformance('result',json);
		}catch(e){}	
		if(isReflow()){
			addEvent(window,'load',function(){
				doReflow(function(json){
					try{
						window.parent.showPerformance('result',json);
					}catch(e){}	
				});
			});
		}
	};

	function sendBeacon(){
			var uriParams = "key=" + getParam("key") + 
							"&browser=" + (browser.safari ? "safari" : 
											browser.opera ? "opera" :
											browser.msie ? "ie" :
											browser.firefox ? "firefox" :
											browser.chrome ? "chrome" :
											browser.android ? "android" :
											browser.ipod ? "ipod" :
											browser.iphone ? "iphone" :
											browser.ipad ? "ipad" :
											"other") +
							"&version=" + browser.version +
							"&minified=" + report.mTime + "&original=" + report.oTime +
							"&minifiedLatency=" + report.mLatency + "&originalLatency=" + report.oLatency +
							 (report.mReflowTime ? "&minifiedReflow=" + report.mReflowTime : "") +
							 (report.oReflowTime ? "&originalReflow=" + report.oReflowTime : "") ;
							//+"&latency=" + _minifier_startTime ;		
			var url = SERVER +"/beacon?" + uriParams + "&rd=" + radom;
			//Opera, Safira and Chrome have some problem with unload event that page sent request to server.
			if( /webkit/.test( userAgent )|| (/opera/.test( userAgent ) && !/msie/.test( userAgent ))){
				xhr = new XMLHttpRequest();
				xhr.open('GET',url,false);
				xhr.send(null);
				return;
			}
			(new Image).src= url;

	};
	//force opera unload event.
	if(/opera/.test( userAgent ) && !/msie/.test( userAgent )){
		opera.setOverrideHistoryNavigationMode('compatible');
		history.navigationMode = 'compatible';
	}
	if(isDisplay() && !isTest()){
		 showDetail();
	}else if(isDisplay() && isTest()){
		window['_doReflow@Show:Minifier_'] = doReflow;
		doPerformance();	
	}else{
		window.$ = $;
		window.addEvent = addEvent;
		window.urlReg = urlReg;
		window.each = each;
		window.getElementsByClassName = getElementsByClassName;
		window.getCookie = getCookie;
		window.setCookie = setCookie;
		window.removeCookie = removeCookie;
		window.nodeToArray = nodeToArray;
		window.showPerformance = showPerformance;
		window.sendBeacon = sendBeacon;
	}


	var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-16776685-1']);
  _gaq.push(['_trackPageview']);

  (function() {
	var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
	ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
	var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();

}(window));